import chess
from chess import InputException, Move, Position, Piece

def main():
  cg = ChessGame()
  cg.reset_game()
  print cg
   
class ThreePositionRuleException(Exception):
  pass
  
class IllegalBoardException(Exception):
  pass


class ChessGame():
  initialized = False
  TEAM_WHITE = chess.TEAM_WHITE
  TEAM_BLACK = chess.TEAM_BLACK
  CHECKMATE = chess.CHECKMATE
  PLAYING = chess.PLAYING
  RESIGNED = chess.RESIGNED
  DRAW = chess.DRAW
  Move = Move
  Position = Position
  Piece = Piece
  def __init__(self):
    if ChessGame.initialized == False:
      ChessGame.initialized = True
      chess.initialize_pieces()
    self.chessboard = chess.Chessboard()
    self.state = {}
    self.position_counter = Counter()

  def reset_game(self):
    self.chessboard.initialize_standard_board()
    self.state['turn'] = chess.TEAM_WHITE
    self.state['status'] = chess.PLAYING 
    self._update_moves()
    self.state['score'] = 0
    self.state['message'] = "Let's play chess!"
    self.state['move_number'] = 0
    self.state['move_history'] = []
    self.position_counter = Counter()
    
  '''
  Try moving. Returns success package. Throws IllegalMoveException.
  '''
  def move(self, move):
    if self.state['status'] == ChessGame.PLAYING:
      if move in self.state['legal_moves']:
        self.state['move_number'] += 1
        self.state['move_history'].append((self.state['move_number'], move))
        self.state['message'] = '%s moved %s' % (self.state['turn'], move)
        self.chessboard.force_move(move, self.state['move_number'])
        self.update_50_move_draw_count(move)
        self.state['king_in_check'] = self.get_king_in_check()
        if move == Move('e1g1'): # White 0-0
          self.chessboard.force_move(Move('h1f1'), self.state['move_number'])
        elif move == Move('e1c1'): # White 0-0-0
          self.chessboard.force_move(Move('a1d1'), self.state['move_number'])
        elif move == Move('e8g8'): # Black 0-0
          self.chessboard.force_move(Move('h8f8'), self.state['move_number'])
        elif move == Move('e8c8'): # Black 0-0-0
          self.chessboard.force_move(Move('a8d8'), self.state['move_number'])
        self._change_turn()
        try:
          self._update_moves()
        except ThreePositionRuleException as e:
          self.state['status'] = ChessGame.DRAW
          self.state['message'] += '\nDraw: Three position rule.'
          self.state['score'] = 0
        if self.state['legal_moves'] == []: #checkmate
          defending_king = WHITE_KING if self.state['turn'] == chess.TEAM_WHITE else BLACK_KING
          if self.state['king_in_check'] == defending_king:
            winning_team = self._opposing_team(self.state['turn'])
            self.state['status'] = chess.CHECKMATE
            self.state['message'] += '\n%s wins!' % str(winning_team)
            self.state['score'] = 1 if winning_team == chess.TEAM_WHITE else -1
          else:
            self.state['status'] = chess.DRAW
            self.state['message'] += 'Stalemate.'
            self.state['score'] = 0
        if self.state['50_move_draw_count'] >= 100:
          self.state['status'] = chess.DRAW
          self.state['message'] += 'Fifty-move rule.'
          self.state['score'] = 0
        
      else:
        self.state['message'] = '"%s" is not a legal move for %s.' \
            % (str(move), str(self.state['turn']))
        raise InputException(self.state['message'])
    else:
      self.state['message'] = "Game Over"
  
  def update_50_move_draw_count(self, move):
    if move.capture:
      self.state['50_move_draw_count'] = 0
    elif is_pawn(self.get_piece(move.end_position)):
      self.state['50_move_draw_count'] = 0
    else:
      self.state['50_move_draw_count'] += 1
      
  def get_king_in_check(self):
    poks = self.chessboard.get_positions_of_kings_in_check()
    if len(poks) > 1:
      raise IllegalBoardException()
    if len(poks) == 0:
      return None
    position = poks[0]
    return self.chessboard.get_piece(position).team
    
  def resign(self):
    self.state['status'] = chess.RESIGNED
    winning_team = self._opposing_team(self.state['turn'])
    self.state['message'] = '%s wins!' % str(winning_team)
    self.state['score'] = 1 if winning_team == chess.TEAM_WHITE else -1

  def _change_turn(self):
    self.state['turn'] = self._opposing_team(self.state['turn'])
    
  def _opposing_team(self, team):
    return ChessGame.TEAM_WHITE if team == ChessGame.TEAM_BLACK else ChessGame.TEAM_BLACK
  
  '''
  Calculate and returns a list of Move
  ''' 
  def _update_moves(self):
    self.state['legal_moves'] = self.chessboard.get_legal_moves(self.state['turn'])
    position_str = str(self)
    self.position_counter[position_str] += 1
    if self.position_counter[position_str] >= 3:
      raise ThreePositionRuleException('Three position rule')
    
  
  def get_prompt(self):
    return '%s please enter a move:' % self.state['turn']

  '''===== SELECTORS ====='''
  
  def get_positions(self):
    positions = []
    for f in xrange(8):
      for r in xrange(8):
        positions.append(Position((f,r)))
    return positions
  
  def get_pieces(self):
    pieces = []
    for p in self.get_positions():
      piece = self.get_piece(p)
      if piece is not None:
        pieces.append(piece)
    return pieces
  
  def get_piece(self, position):
    return self.chessboard.get_piece(position)
    
  def __str__(self):
    return str(self.chessboard)
  
  def copy(self):
    new_game = ChessGame()
    new_game.chessboard = self.chessboard.copy()
    new_game.state = self.state.copy()
    return new_game 

def is_pawn(piece):
  return piece == chess.WHITE_PAWN or chess.BLACK_PAWN
"""
def test_castling_moves_0_0():
  return '''e2e4
e7e5
f1c4
f8c5
g1f3
g8f6
e1g1
e8g8
'''

def test_castling_moves_0_0_0():
  return '''d2d4
d7d5
c1f4
c8f5
b1c3
b8c6
d1d2
d8d7
e1c1
e8c8
'''
"""


class Counter(dict):
  """
  A counter keeps track of counts for a set of keys.
  
  The counter class is an extension of the standard python
  dictionary type.  It is specialized to have number values  
  (integers or floats), and includes a handful of additional
  functions to ease the task of counting data.  In particular, 
  all keys are defaulted to have value 0.  Using a dictionary:
  
  a = {}
  print a['test']
  
  would give an error, while the Counter class analogue:
    
  >>> a = Counter()
  >>> print a['test']
  0

  returns the default 0 value. Note that to reference a key 
  that you know is contained in the counter, 
  you can still use the dictionary syntax:
    
  >>> a = Counter()
  >>> a['test'] = 2
  >>> print a['test']
  2
  
  This is very useful for counting things without initializing their counts,
  see for example:
  
  >>> a['blah'] += 1
  >>> print a['blah']
  1
  
  The counter also includes additional functionality useful in implementing
  the classifiers for this assignment.  Two counters can be added,
  subtracted or multiplied together.  See below for details.  They can
  also be normalized and their total count and arg max can be extracted.
  """
  def __getitem__(self, idx):
    self.setdefault(idx, 0)
    return dict.__getitem__(self, idx)

  def incrementAll(self, keys, count):
    """
    Increments all elements of keys by the same count.
    
    >>> a = Counter()
    >>> a.incrementAll(['one','two', 'three'], 1)
    >>> a['one']
    1
    >>> a['two']
    1
    """
    for key in keys:
      self[key] += count
  
  def argMax(self):
    """
    Returns the key with the highest value.
    """
    if len(self.keys()) == 0: return None
    all = self.items()
    values = [x[1] for x in all]
    maxIndex = values.index(max(values))
    return all[maxIndex][0]
  
  def sortedKeys(self):
    """
    Returns a list of keys sorted by their values.  Keys
    with the highest values will appear first.
    
    >>> a = Counter()
    >>> a['first'] = -2
    >>> a['second'] = 4
    >>> a['third'] = 1
    >>> a.sortedKeys()
    ['second', 'third', 'first']
    """
    sortedItems = self.items()
    compare = lambda x, y:  sign(y[1] - x[1])
    sortedItems.sort(cmp=compare)
    return [x[0] for x in sortedItems]
  
  def totalCount(self):
    """
    Returns the sum of counts for all keys.
    """
    return sum(self.values())
  
  def normalize(self):
    """
    Edits the counter such that the total count of all
    keys sums to 1.  The ratio of counts for all keys
    will remain the same. Note that normalizing an empty 
    Counter will result in an error.
    """
    total = float(self.totalCount())
    if total == 0: return
    for key in self.keys():
      self[key] = self[key] / total
      
  def divideAll(self, divisor):
    """
    Divides all counts by divisor
    """
    divisor = float(divisor)
    for key in self:
      self[key] /= divisor

  def copy(self):
    """
    Returns a copy of the counter
    """
    return Counter(dict.copy(self))
  
  def __mul__(self, y ):
    """
    Multiplying two counters gives the dot product of their vectors where
    each unique label is a vector element.
    
    >>> a = Counter()
    >>> b = Counter()
    >>> a['first'] = -2
    >>> a['second'] = 4
    >>> b['first'] = 3
    >>> b['second'] = 5
    >>> a['third'] = 1.5
    >>> a['fourth'] = 2.5
    >>> a * b
    14
    """
    sum = 0
    x = self
    if len(x) > len(y):
      x,y = y,x
    for key in x:
      if key not in y:
        continue
      sum += x[key] * y[key]      
    return sum
      
  def __radd__(self, y):
    """
    Adding another counter to a counter increments the current counter
    by the values stored in the second counter.
    
    >>> a = Counter()
    >>> b = Counter()
    >>> a['first'] = -2
    >>> a['second'] = 4
    >>> b['first'] = 3
    >>> b['third'] = 1
    >>> a += b
    >>> a['first']
    1
    """ 
    for key, value in y.items():
      self[key] += value   
      
  def __add__( self, y ):
    """
    Adding two counters gives a counter with the union of all keys and
    counts of the second added to counts of the first.
    
    >>> a = Counter()
    >>> b = Counter()
    >>> a['first'] = -2
    >>> a['second'] = 4
    >>> b['first'] = 3
    >>> b['third'] = 1
    >>> (a + b)['first']
    1
    """
    addend = Counter()
    for key in self:
      if key in y:
        addend[key] = self[key] + y[key]
      else:
        addend[key] = self[key]
    for key in y:
      if key in self:
        continue
      addend[key] = y[key]
    return addend
    
  def __sub__( self, y ):
    """
    Subtracting a counter from another gives a counter with the union of all keys and
    counts of the second subtracted from counts of the first.
    
    >>> a = Counter()
    >>> b = Counter()
    >>> a['first'] = -2
    >>> a['second'] = 4
    >>> b['first'] = 3
    >>> b['third'] = 1
    >>> (a - b)['first']
    -5
    """      
    addend = Counter()
    for key in self:
      if key in y:
        addend[key] = self[key] - y[key]
      else:
        addend[key] = self[key]
    for key in y:
      if key in self:
        continue
      addend[key] = -1 * y[key]
    return addend
 
if __name__ == '__main__':
  main()